package com.kalle.workflow.service.impl;

import com.kalle.workflow.elements.Deployment;
import com.kalle.workflow.elements.Flow;
import com.kalle.workflow.elements.Process;
import com.kalle.workflow.elements.Task;
import com.kalle.workflow.enums.FlowStatus;
import com.kalle.workflow.enums.ProcessStatus;
import com.kalle.workflow.mapper.DeploymentMapper;
import com.kalle.workflow.mapper.FlowMapper;
import com.kalle.workflow.mapper.ProcessMapper;
import com.kalle.workflow.mapper.TaskMapper;
import com.kalle.workflow.service.ProcessService;
import com.kalle.workflow.util.IdGenerator;
import com.kalle.workflow.util.UUIDGenerator;
import org.apache.ibatis.session.SqlSession;

import java.util.Date;
import java.util.Optional;

public class ProcessServiceImpl implements ProcessService {

    private final SqlSession sqlSession;
    private final IdGenerator idGenerator;

    private final ProcessMapper processMapper;
    private final DeploymentMapper deploymentMapper;
    private final FlowMapper flowMapper;
    private final TaskMapper taskMapper;

    public ProcessServiceImpl(SqlSession sqlSession, IdGenerator idGenerator) {
        this.sqlSession = sqlSession;
        this.processMapper = sqlSession.getMapper(ProcessMapper.class);
        this.deploymentMapper = sqlSession.getMapper(DeploymentMapper.class);
        this.flowMapper = sqlSession.getMapper(FlowMapper.class);
        this.taskMapper = sqlSession.getMapper(TaskMapper.class);
        this.idGenerator = Optional.ofNullable(idGenerator).orElse(UUIDGenerator.INSTANCE);
    }

    @Override
    public String startProcessByDeploymentId(String deploymentId) {
        Deployment deployment = deploymentMapper.selectById(deploymentId);
        if (null == deployment) {
            throw new RuntimeException("Deployment not found with deploymentId: " + deploymentId);
        }
        Process process = insertProcess(deployment);
        return process.getId();
    }

    @Override
    public String startProcessByProcessKey(String processKey) {
        Deployment deployment = deploymentMapper.selectByProcessKey(processKey);
        if (null == deployment) {
            throw new RuntimeException("Deployment not found with processKey: " + processKey);
        }
        Process process = insertProcess(deployment);
        return process.getId();
    }

    private Process insertProcess(Deployment deployment) {
        Process process = new Process();
        process.setId(idGenerator.nextId());
        process.setDeploymentId(deployment.getId());
        process.setProcessStatus(ProcessStatus.ACTIVE);
        process.setCreateTime(new Date());
        processMapper.insert(process);
        //find start event
        Task startEvent = taskMapper.getStartEventByDeployId(deployment.getId());
        //create first flow
        Flow flow = new Flow();
        flow.setId(idGenerator.nextId());
        flow.setDeploymentId(deployment.getId());
        flow.setProcessId(process.getId());
        flow.setTaskId(startEvent.getId());
        flow.setFlowStatus(FlowStatus.ACTIVE);
        flow.setCreateTime(new Date());
        flowMapper.insert(flow);
        sqlSession.commit();
        return process;
    }

}
